/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *        http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
#include <openssl/pem.h>
#include <openssl/cms.h>
#include <openssl/err.h>
#include <openssl/crypto.h>
#include <getopt.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include "public.h"

#define TS_FILE ".temp.cms.timestamp.der"
#define TAS_FILE ".temp.cms.hash.tsa"
#define CMS_HASH_FILE ".temp.cms.hash.der"

const char     *signer_file = "signer.pem";
const char     *in_file     = "sign.txt";
const char     *out_file    = "signed.cms";
const char     *ts_signer   = "ts_signer.pem";
const char     *tsa_cnf     = "tsa.cnf";
const char     *password    = NULL;
static int      verbose     = 0;
static BUF_MEM *cms_tmp_buf = NULL;

#define SALT_FLAG "Salted__"

static void unlink_file(const char *name)
{
    if (verbose == 0) {
        unlink(name);
    }
}

static void print_help(const char *exec_name)
{
    log_debug("Usage: %s [options]", exec_name);
    log_debug("Valid options are:");
    log_debug(" %-32s Signer certificate file\n%*c  > default: signer.pem", "-s, --signer=FILE", 32, ' ');
    log_debug(" %-32s Input file\n%*c  > default: sign.txt", "-i, --input=FILE", 32, ' ');
    log_debug(" %-32s Output file\n%*c  > default: signed.cms", "-o, --output=FILE", 32, ' ');
    log_debug(" %-32s Timestamp signer certificate file\n%*c  > default: ts_signer.pem", "-t, --ts_signer", 32, ' ');
    log_debug(" %-32s Timestamp config file\n%*c  > default: tsa.cnf", "-T, --tsa_cnf", 32, ' ');
    log_debug(" %-32s aes-256 encrypted password, also support environment variable $SIGN_PASSWORD\n%*c  > default:",
              "-p, --password", 32, ' ');
    log_debug(" %-32s Print debug messages", "-v, --verbose");
    log_debug(" %-32s Display this summary", "-h, --help");
}

static const char         *opt_string = "s:i:o:t:T:p:vh";
static const struct option opts[]     = {
    {"signer", required_argument, NULL, 's'},  {"in", required_argument, NULL, 'i'},
    {"out", required_argument, NULL, 'o'},     {"ts_signer", required_argument, NULL, 't'},
    {"tsa_cnf", required_argument, NULL, 'T'}, {"verbose", no_argument, NULL, 'v'},
    {"help", no_argument, NULL, 'h'},          {NULL},
};

static unsigned char *read_file(const char *filename, int *read_len, unsigned char *salt, size_t salt_len)
{
    unsigned char *buffer = NULL;
    unsigned char *output = NULL;
    *read_len             = 0;

    FILE *fp = fopen(filename, "r");
    if (!fp) {
        log_error("Open file %s failed, error: %s", filename, strerror(errno));
        return NULL;
    }
    do {
        int ret = fseek(fp, 0, SEEK_END);
        if (ret != 0) {
            log_error("Seek file %s failed, error: %s", filename, strerror(errno));
            break;
        }
        size_t pos = (size_t)ftell(fp);
        (void)fseek(fp, 0, SEEK_SET);
        if (ret != 0) {
            log_error("Seek file %s failed, error: %s", filename, strerror(errno));
            break;
        }
        buffer      = malloc(pos + 1);
        if (buffer == NULL) {
            log_error("malloc memory failed");
            break;
        }
        buffer[pos] = '\0';
        ret         = (int)fread(buffer, 1, pos, fp);
        if (ret != (int)pos) {
            log_error("Read file %s failed, error: %s", signer_file, strerror(errno));
            break;
        }
        if (pos >= (strlen(SALT_FLAG) + salt_len) && memcmp(buffer, SALT_FLAG, strlen(SALT_FLAG)) == 0) {
            (void)memcpy(salt, buffer + strlen(SALT_FLAG), salt_len);
            *read_len = (int)(pos - strlen(SALT_FLAG) - salt_len);
            memmove(buffer, buffer + strlen(SALT_FLAG) + salt_len, (size_t)(*read_len));
        } else {
            *read_len = (int)pos;
        }
        output = buffer;
        buffer = NULL;
        log_debug("Read data len: %d", *read_len);
    } while (0);
    (void)fclose(fp);
    if (buffer) {
        free(buffer);
    }
    return output;
}

static BIO *read_signer_file(const char *filename)
{
    EVP_CIPHER_CTX *ctx;
    unsigned char   salt[8]                                    = {0};
    unsigned char   kv[EVP_MAX_KEY_LENGTH + EVP_MAX_IV_LENGTH] = {0};
    int             plain_len                                  = 0;
    int             len                                        = 0;
    BIO            *bio                                        = NULL;
    unsigned char  *plain_buffer                               = NULL;

    int            read_len    = 0;
    unsigned char *read_buffer = read_file(filename, &read_len, salt, sizeof(salt));
    if (!read_buffer) {
        return NULL;
    }
    log_debug("read file %s, return len: %d", filename, read_len);
    do {
        ctx = EVP_CIPHER_CTX_new();
        if (!password) {
            bio = BIO_new(BIO_s_mem());
            (void)BIO_write(bio, read_buffer, read_len);
            break;
        }
        const EVP_CIPHER *cipher = EVP_aes_256_cbc();
        int               klen   = EVP_CIPHER_key_length(cipher);
        int               vlen   = EVP_CIPHER_iv_length(cipher);
        PKCS5_PBKDF2_HMAC(password, -1, salt, sizeof(salt), 10000, EVP_sha256(), klen + vlen, kv);
        int ret = EVP_DecryptInit_ex(ctx, cipher, NULL, kv, kv + klen);
        if (ret <= 0) {
            log_error("Call EVP_DecryptInit_ex with error");
            break;
        }
        plain_buffer = malloc((size_t)read_len + 1);
        memset(plain_buffer, 0, (size_t)read_len + 1);
        ret = EVP_DecryptUpdate(ctx, plain_buffer, &plain_len, read_buffer, read_len);
        if (ret <= 0) {
            log_error("Call EVP_DecryptUpdate with error");
            break;
        }
        ret = EVP_DecryptFinal_ex(ctx, plain_buffer + plain_len, &len);
        if (ret <= 0) {
            log_error("Call EVP_DecryptFinal_ex with error");
            break;
        }
        plain_len += len;
        bio = BIO_new(BIO_s_mem());
        (void)BIO_write(bio, plain_buffer, plain_len);
        log_debug("Decrypt data len: %d", plain_len);
    } while (0);
    free(read_buffer);
    EVP_CIPHER_CTX_reset(ctx);
    EVP_CIPHER_CTX_free(ctx);
    if (plain_buffer) {
        free(plain_buffer);
    }
    return bio;
}

static int cms_sign(void)
{
    BIO             *in = NULL, *out = NULL, *tbio = NULL;
    X509            *scert = NULL;
    EVP_PKEY        *skey  = NULL;
    CMS_ContentInfo *cms   = NULL;
    int              ret   = -1;

    log_info("Sign %s start", in_file);
    int flags = CMS_DETACHED | CMS_NOSMIMECAP | CMS_BINARY;

    OpenSSL_add_all_algorithms();
    ERR_load_crypto_strings();

    do {
        log_debug("Open signer certificate: %s", signer_file);
        tbio = read_signer_file(signer_file);
        if (!tbio) {
            break;
        }

        log_debug("Read signer certificate: %s", signer_file);
        scert = PEM_read_bio_X509(tbio, NULL, 0, NULL);
        log_debug("Read private key: %s", signer_file);
        skey = PEM_read_bio_PrivateKey(tbio, NULL, 0, NULL);
        BIO_reset(tbio);
        if (!scert || !skey) {
            break;
        }

        log_debug("Open content being signed: %s", in_file);
        in = BIO_new_file(in_file, "r");
        if (!in) {
            log_error("Open content file %s failed", in_file);
            break;
        }

        out = BIO_new(BIO_s_mem());
        if (!out) {
            log_error("Create cms output buffer failed");
            break;
        }

        log_debug("Sign content");
        cms = CMS_sign(scert, skey, NULL, in, (unsigned int)flags);
        if (!cms) {
            log_error("Sign content failed");
            break;
        }

        log_debug("Write signed content");
        if (i2d_CMS_bio_stream(out, cms, in, flags) == 0) {
            log_error("Write signed content failed");
            break;
        }
        BIO_get_mem_ptr(out, &cms_tmp_buf);
        BIO_set_close(out, BIO_NOCLOSE); /* So BIO_free() leaves BUF_MEM alone */
        log_info("Sign %s successfully(need timestamp sign more)", in_file);
        ret = 0;
    } while (0);

    if (ret != 0) {
        log_error("Error Signing Data:");
        print_ssl_error();
    }

    CMS_ContentInfo_free(cms);
    X509_free(scert);
    EVP_PKEY_free(skey);
    BIO_free(in);
    BIO_free(out);
    BIO_free(tbio);
    return ret;
}

struct asn_data {
    struct asn1_string_st asn;
    int                   real_length;  // asn剔除了子节点的数据后的长度
    int                   cap;
    int                   n;
    struct asn_data     **next;
    struct asn_data      *prev;
};

static int calc_asn_head_size(int in_length)
{
    int length = in_length;
    int size = 2;  // type+length至少2个字节
    // 不足1字节的使用短表示
    if (length <= 0x7f) {
        return size;
    }
    // 多于1字节的使用长表示
    while (length > 0) {
        size += 1;
        length = length / 256;  // 每字节表示256个数
    }
    return size;
}

static struct asn_data cms_ad;

/**
 * 新增下层节点时递归修改父节点的长度
 */
static void add_data_length_recurse(struct asn_data *tmp_ad, int in_length)
{
    int length = in_length;
    if (tmp_ad == (&cms_ad)) {
        return;
    }
    do {
        int old_length    = tmp_ad->asn.length;
        int old_head_size = calc_asn_head_size(tmp_ad->asn.length);
        tmp_ad->asn.length += length;
        int new_head_size = calc_asn_head_size(tmp_ad->asn.length);
        length += (new_head_size - old_head_size);
        tmp_ad = tmp_ad->prev;
    } while (tmp_ad && tmp_ad != (&cms_ad));
}

/**
 * 新增下层节点
 */
static struct asn_data *append_asn_data(struct asn_data *ad, const struct asn1_string_st *asn, int add_parent_length)
{
    if (ad->n >= ad->cap) {
        ad->cap += 1;
        if (ad->next != NULL) {
            size_t len = sizeof(struct asn_data *) * ad->cap;
            struct asn_data **newNext = (struct asn_data **)malloc(len);
            memset(newNext, 0, len);
            memcpy(newNext, ad->next, sizeof(struct asn_data *) * (ad->cap - 1));
            free(ad->next);
            ad->next = newNext;
        } else {
            ad->next = (struct asn_data **)malloc((size_t)ad->cap);
        }
        if (ad->next == NULL) {
            log_error("malloc memory failed");
            return NULL;
        }
    }
    struct asn_data *next = (struct asn_data *)malloc(sizeof(struct asn_data));
    if (next == NULL) {
        log_error("malloc memory failed");
        return NULL;
    }
    ad->next[ad->n]       = next;
    memset(next, 0, sizeof(*next));
    memcpy(&next->asn, asn, sizeof(*asn));
    next->prev = ad;
    // 节点的真实大小默认先设置为内容大小，加载完成后需要使用fixed_asn_data_length调整
    next->real_length = asn->length;
    ad->n++;
    if (add_parent_length != 0) {
        add_data_length_recurse(ad, (asn->length + calc_asn_head_size(asn->length)));
    }
    return next;
}

/**
 * 调整节点自身数据真实大小
 */
static struct asn_data *fixed_asn_real_length(struct asn_data *ad)
{
    for (int i = 0; i < ad->n; i++) {
        struct asn_data *next = ad->next[i];
        fixed_asn_real_length(next);
        ad->real_length -= (next->asn.length + calc_asn_head_size(next->asn.length));
    }
}

/**
 * 打印ASA数据
 */
static struct asn_data *show_asn_info(int in_ident, struct asn_data *ad)
{
    int ident = in_ident;
    ident += 4;  // 显示缩进设置为4
    log_debug_ident(ident, "ASN INFO, type: 0x%02x, length: 0x%04x, self data lenght: 0x%04x",
                    (unsigned int)ad->asn.type, (unsigned int)ad->asn.length, ad->real_length);
    for (int i = 0; i < ad->n; i++) {
        struct asn_data *next = ad->next[i];
        // 已经以是最后一个节点
        show_asn_info(ident, next);
    }
}

/**
 * 从内存中加载asn数据结构
 */
static int asn_parse(int ident, unsigned char *buf, size_t read_len, struct asn_data *ad)
{
    int                   ret = 0;
    struct asn1_string_st asn;
    for (int i = 0; i < (int)read_len;) {
        memset(&asn, 0, sizeof(asn));
        asn.type = (int)buf[i];
        int j    = 0;
        if ((buf[i + 1] & 0x80) != 0) {
            for (j = 0; j < (buf[i + 1] & 0x7f); j++) {
                // 右移8位
                asn.length = (int)(((unsigned int)asn.length << 8) | buf[i + 2 + j]);
            }
        } else {
            asn.length = (int)buf[i + 1];
        }
        if (asn.length + 2 + j > (int)read_len) {
            log_debug_ident(ident, "content length overlay, need: %d, length: %lu", asn.length + 2 + j, read_len);
            ret = -1;
            break;
        }
        asn.data = buf + i + 2 + j;
        (void)append_asn_data(ad, &asn, 0);
        if (asn.type == 0x30 || asn.type == 0xa0 || asn.type == 0x31) {
            ret = asn_parse(ident + 2, buf + i + 2 + j, (size_t)asn.length, ad->next[ad->n - 1]);
            if (ret != 0) {
                break;
            }
        }
        i += asn.length + 2 + j;
    }

    if (ident == 0) {
        fixed_asn_real_length(ad);
        show_asn_info(0, ad->next[0]);
    }
    return ret;
}

/**
 * 获取签名hash
 */
static unsigned char *get_rsa_encryption(struct asn_data *ad, int *len)
{
    unsigned char *buf = NULL;
    if (ad->asn.type == 0x04 && ad->asn.length == 0x200) {
        buf = (unsigned char *)malloc(0x200);
        if (buf == NULL) {
            log_error("malloc memory failed");
            return NULL;
        }

        memcpy(buf, ad->asn.data, 0x200);
        *len = 0x200;
        return buf;
    }
    for (int i = 0; i < ad->n; i++) {
        struct asn_data *next = ad->next[i];
        buf                   = get_rsa_encryption(next, len);
        if (buf != NULL) {
            return buf;
        }
    }
    return NULL;
}

/**
 * 生成时间戳签名请求
 */
static int tsa_request(const char *cms_hash_file, const char *tsa_file)
{
    char cmd[4096] = {0};
    log_info("Generate tsa request file");
    int ret = snprintf(cmd, sizeof(cmd) - 1,
        "openssl ts -query -data %s -cert -sha256 -no_nonce -out %s", cms_hash_file, tsa_file);
    if (ret <= 0) {
        log_error("Call snprintf failed");
        return -1;
    }
    ret   = system(cmd);
    if (ret != 0) {
        log_error("Call command %s failed", cmd);
        return -1;
    }
    return 0;
}

/**
 * 执行时间戳签名
 */
static int tsa_signed(const char *tsa_file, const char *signed_file)
{
    char cmd[4096]  = {0};
    char template[] = "tmp.29dd2162b9f8-XXXXXX";
    int fd          = mkstemp(template);
    if (fd <= 0) {
        log_error("Create temp file failed, error: %s", strerror(errno));
        return -1;
    }
    BIO *tbio = read_signer_file(ts_signer);
    if (!tbio) {
        close(fd);
        return -1;
    }
    char *mem_ptr = NULL;
    long  size    = BIO_get_mem_data(tbio, &mem_ptr);
    log_debug("Decrypt data : %s", mem_ptr);
    write(fd, mem_ptr, (size_t)size);
    BIO_reset(tbio);
    BIO_free(tbio);
    log_info("Signed tsa request file");
    int ret = snprintf(cmd,  sizeof(cmd) - 1,
                       "openssl ts -config %s -reply -config %s -queryfile %s -signer %s -inkey %s -out %s", tsa_cnf,
                       tsa_cnf, tsa_file, template, template, signed_file);
    close(fd);
    if (ret <= 0) {
        unlink(template);
        log_error("Call snprintf failed");
        return -1;
    }
    ret = system(cmd);
    unlink(template);
    if (ret != 0) {
        log_error("Call command %s failed", cmd);
        return -1;
    }
    return 0;
}

/**
 * 读取文件内容
 */
static unsigned char *read_content(const char *cms_file, size_t *read_len)
{
    unsigned char        *buf = NULL;
    struct asn1_string_st asn;
    FILE                 *fp = fopen(cms_file, "rb");
    if (fp == NULL) {
        log_error("Open file %s failed, error: %s", cms_file, strerror(errno));
        return NULL;
    }
    (void)fseek(fp, 0L, SEEK_END);
    size_t pos = (size_t)ftell(fp);
    (void)fseek(fp, 0L, SEEK_SET);
    buf       = (unsigned char *)malloc(pos);
    if (buf == NULL) {
        log_error("malloc memory failed");
        (void)fclose(fp);
        return NULL;
    }
    *read_len = fread(buf, 1, pos, fp);
    (void)fclose(fp);
    if (*read_len != pos) {
        log_error("Read file %s content not finished, broken", cms_file);
        free(buf);
        return NULL;
    }
    return buf;
}

/**
 * 记录OID信息，用于asn中查找path
 */
struct oid_info {
    const char          *name;      // 名称
    const unsigned char *oid;       // oid数组
    size_t               length;    // oid长度
    struct asn_data     *ad;        // asn
    unsigned int         path[32];  // 32个长度足够
    int                  idx;       // 查找第几个oid
};
#define OID_LAST_IDX 0xffff  // 表示获取最后一个OID 65535个足够

/**
 * 从asn中查找指定oid的path
 * path规则为首字符表示path的长度（从1计数，0表示不存在）
 */
static void find_path_by_oid(struct asn_data *ad, struct oid_info *oi, unsigned int *tmp_path, int tmp_len,
                             size_t parent_idx)
{
    tmp_path[tmp_path[0]] = (unsigned int)parent_idx;
    if (ad->asn.type == 0x06 && ad->asn.length == (int)oi->length) {
        if (memcmp(oi->oid, ad->asn.data, oi->length) == 0) {
            if (oi->idx == OID_LAST_IDX) {
                // 获取最后一个匹配的OID，则每次都复制
                memcpy(oi->path, tmp_path, (size_t)tmp_len);
                oi->ad = ad;
            } else {
                oi->idx--;
                if (oi->idx == 0) {
                    memcpy(oi->path, tmp_path, (size_t)tmp_len);
                    oi->ad = ad;
                }
            }
        }
    }
    for (int i = 0; i < ad->n; i++) {
        struct asn_data *next = ad->next[i];
        tmp_path[0]           = tmp_path[0] + 1;
        find_path_by_oid(next, oi, tmp_path, tmp_len, (size_t)i);
        // 未找到，长度减一
        tmp_path[0] = tmp_path[0] - 1;
    }
}

/**
 * 打印path
 */
static void show_path(const struct oid_info *oi)
{
    char str[256] = {0};
    for (unsigned int i = 2; i <= oi->path[0]; i++) {
        size_t str_length = sizeof(str) - strlen(str);
        if (i != 2) {
            snprintf(str + strlen(str), str_length, "/%u", oi->path[i]);
        } else {
            snprintf(str + strlen(str), str_length, "%u", oi->path[i]);
        }
    }
    log_debug("Oid info, %s: %s", oi->name, str);
}

// cms签名的加密数据
static const unsigned char rsa_encryption_oid[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01};
// ts的签名数据
static const unsigned char signed_data_oid[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02};
// timestamp oid
static const unsigned char timestamp_oid[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x10, 0x02, 0x0e};

static unsigned char *dump_memory(const unsigned char *data, size_t len)
{
    unsigned char *tmp = malloc(len);
    if (tmp == NULL) {
        log_error("malloc memory failed");
        return NULL;
    }
    memcpy(tmp, data, len);
    return tmp;
}

static void free_ts_asn(struct asn1_string_st *ctx_specific, struct asn1_string_st *sequence_asn,
    struct asn1_string_st *oid_asn, struct asn1_string_st *set_asn)
{
    if (ctx_specific != NULL) {
        free(ctx_specific);
        ctx_specific = NULL;
    }

    if (sequence_asn != NULL) {
        free(sequence_asn);
        sequence_asn = NULL;
    }

    if (oid_asn != NULL) {
        free(oid_asn);
        oid_asn = NULL;
    }

    if (set_asn != NULL) {
        free(set_asn);
        set_asn = NULL;
    }
}

/**
 * 向cms签名文件中追加时间戳签名
 */
static void append_ts_asn(struct asn_data *cms_context_ad, struct asn_data *ts_context_ad)
{
    struct asn1_string_st *ctx_specific = NULL;
    struct asn1_string_st *sequence_asn = NULL;
    struct asn1_string_st *oid_asn = NULL;
    struct asn1_string_st *set_asn = NULL;
    // 0xa0
    do {
        ctx_specific = (struct asn1_string_st *)malloc(sizeof(struct asn1_string_st));
        if (ctx_specific == NULL) {
            log_error("ctx_specific malloc mem failed");
            break;
        }
        memset(ctx_specific, 0, sizeof(struct asn1_string_st));
        ctx_specific->type               = 0xA1;
        ctx_specific->length             = 0;
        struct asn_data *ctx_specific_ad = append_asn_data(cms_context_ad, ctx_specific, 1);

        // 0xa0 -> 0x30
        sequence_asn = (struct asn1_string_st *)malloc(sizeof(struct asn1_string_st));
        if (sequence_asn == NULL) {
            log_error("sequence_asn malloc mem failed");
            break;
        }
        memset(sequence_asn, 0, sizeof(struct asn1_string_st));
        sequence_asn->type               = 0x30;
        sequence_asn->length             = 0;
        struct asn_data *sequence_asn_ad = append_asn_data(ctx_specific_ad, sequence_asn, 1);

        // 0xa0 -> 0x30 -> 0x06
        oid_asn = (struct asn1_string_st *)malloc(sizeof(struct asn1_string_st));
        if (oid_asn == NULL) {
            log_error("oid_asn malloc mem failed");
            break;
        }
        memset(oid_asn, 0, sizeof(struct asn1_string_st));
        oid_asn->type   = 0x06;
        oid_asn->length = (int)sizeof(timestamp_oid);
        oid_asn->data   = dump_memory(timestamp_oid, sizeof(timestamp_oid));
        (void)append_asn_data(sequence_asn_ad, oid_asn, 1);

        // 0xa0 -> 0x30 -> 0x06
        //      \_ 0x31
        set_asn = (struct asn1_string_st *)malloc(sizeof(struct asn1_string_st));
        if (set_asn == NULL) {
            log_error("set_asn malloc mem failed");
            break;
        }
        memset(set_asn, 0, sizeof(struct asn1_string_st));
        set_asn->type               = 0x31;
        set_asn->length             = 0;
        struct asn_data *set_asn_ad = append_asn_data(sequence_asn_ad, set_asn, 1);

        // 0xa0 -> 0x30 -> 0x06
        //      \_ 0x31 -> 0x30
        int tmp_len                = ts_context_ad->real_length;
        ts_context_ad->real_length = ts_context_ad->asn.length;
        (void)append_asn_data(set_asn_ad, &ts_context_ad->asn, 1);
        ts_context_ad->real_length = tmp_len;
    } while (0);

    free_ts_asn(ctx_specific, sequence_asn, oid_asn, set_asn);
}

/**
 * 合并CMS签名和时间戳签名
 */
static int timestamp_sign_merge(struct asn_data *tsa_ad)
{
    log_info("Merge timestamp signed data");
    unsigned int    path[32] = {0};
    struct oid_info cms_oi   = {
        .name   = "rspEncryption",
        .oid    = rsa_encryption_oid,
        .length = sizeof(rsa_encryption_oid),
        .idx    = OID_LAST_IDX,
    };
    find_path_by_oid(&cms_ad, &cms_oi, path, sizeof(path), 0);
    if (cms_oi.ad == NULL) {
        log_error("Can't find the rsa_encryption_oid path");
        return -1;
    }
    show_path(&cms_oi);
    struct asn_data *cms_context_ad = cms_oi.ad->prev->prev;  // 指向父对象的下一个对象
    log_debug("Cms append position, type: 0x%02x, length: 0x%04x", cms_context_ad->asn.type,
              cms_context_ad->asn.length);
    struct oid_info ts_oi = {
        .name   = "Timestamp signedData",
        .oid    = signed_data_oid,
        .length = sizeof(signed_data_oid),
        .idx    = 1,
    };
    memset(path, 0, sizeof(path));
    find_path_by_oid(tsa_ad, &ts_oi, path, sizeof(path), 0);
    if (ts_oi.ad == NULL) {
        log_error("Can't find the signed_data_oid path");
        return -1;
    }
    show_path(&ts_oi);
    struct asn_data *ts_context_ad = ts_oi.ad->prev;  // 时间戳签名内容为signed_data_oid的父对象
    log_debug("Timestamp sign data, type: 0x%02x, length: 0x%04x", ts_context_ad->asn.type, ts_context_ad->asn.length);

    // 追加时间戳签名
    append_ts_asn(cms_context_ad, ts_context_ad);
    return 0;
}

/**
 * 将asn数据写入文件
 */
static void as_write_st(int ident, FILE *fp, const struct asn_data *ad)
{
    const struct asn1_string_st *asn = &ad->asn;
    // 写类型
    unsigned char buf[4] = {0};  // 4个字符足够存储长度
    int  size   = 0;
    (void)fwrite(&asn->type, 1, 1, fp);
    if (asn->length <= 127) {
        buf[size] = (unsigned char)asn->length;
        size++;
        log_debug_ident(ident, "write type: 0x%02x, length: 0x%04x, real length: 0x%04x", asn->type, asn->length,
                        ad->real_length);
    } else {
        int length = asn->length;
        while (length > 0) {
            buf[size] = length % 256;  // 低字节在前
            length    = length / 256;  // 除256
            size++;
        }
        unsigned char pos0 = 0x80 | (unsigned char)size;
        fwrite(&pos0, 1, 1, fp);
        log_debug_ident(ident, "write type: 0x%02x, length: 0x%02x%04x, real length: 0x%04x", asn->type, pos0,
                        asn->length, ad->real_length);
    }
    for (int i = size - 1; i >= 0; i--) {
        (void)fwrite(buf + i, 1, 1, fp);
    }
    if (ad->real_length > 0) {
        fwrite(asn->data, (size_t)ad->real_length, 1, fp);
    }
}

/**
 * 将asn_data写入文件后递归写入子节点
 */
static void asn_write(int in_ident, FILE *fp, const struct asn_data *ad)
{
    int ident = in_ident;
    ident += 4;  // 显示缩进设置为4
    as_write_st(ident, fp, ad);
    for (int i = 0; i < ad->n; i++) {
        struct asn_data *next = ad->next[i];
        asn_write(ident, fp, next);
    }
}

/**
 * 将cms写入文件
 */
static int cms_write(const char *file)
{
    FILE *fp = fopen(file, "w");
    log_info("Write cms data to %s start", file);
    if (fp == NULL) {
        log_error("Open file %s failed, error: %s", file, strerror(errno));
        return -1;
    }
    asn_write(0, fp, cms_ad.next[0]);
    (void)fclose(fp);
    log_info("Write cms data to %s successfully", file);
    return 0;
}

static int read_tsa(void)
{
    // 读取tsa内容，无需释放
    size_t tsa_len = 0;
    unsigned char *tsa_data = read_content(TS_FILE, &tsa_len);
    unlink_file(TS_FILE);
    if (tsa_data == NULL) {
        return -1;
    }
    struct asn_data tsa_ad;
    memset(&tsa_ad, 0, sizeof(tsa_ad));
    log_info("Parse timestamp signed data");
    if (asn_parse(0, tsa_data, tsa_len, &tsa_ad) != 0) {
        return -1;
    }
    if (timestamp_sign_merge(&tsa_ad) != 0) {
        return -1;
    }

    return 0;
}

/**
 * 时间戳签名流程
 */
static int timestamp_sign(void)
{
    int            len  = 0;
    unsigned char *hash = get_rsa_encryption(&cms_ad, &len);
    if (hash == NULL) {
        log_error("Get rspEncryption data failed");
        return -1;
    }

    log_info("Found rsaEncryption data");
    int flag = -1;
    do {
        FILE  *fp = fopen(CMS_HASH_FILE, "w");
        if (fp == NULL) {
            log_error("open cms_hash_file failed");
            break;
        }
        size_t write_len = fwrite(hash, 1, (size_t)len, fp);
        (void)fclose(fp);
        if ((int)write_len != len) {
            log_error("Write %s failed, error: %s", CMS_HASH_FILE, strerror(errno));
            break;
        }
        // 创建tsa签名请求
        int ret = tsa_request(CMS_HASH_FILE, TAS_FILE);
        unlink_file(CMS_HASH_FILE);
        if (ret != 0) {
            break;
        }
        // 时间戳签名
        ret = tsa_signed(TAS_FILE, TS_FILE);
        unlink_file(TAS_FILE);
        if (ret != 0) {
            break;
        }

        if (read_tsa() != 0) {
            break;
        }

        flag = 0;
    } while (0);

    free(hash);
    hash = NULL;
    if (flag != 0) {
        return flag;
    }
    return cms_write(out_file);
}

static int arg_parse(int argc, char **argv)
{
    int opt = 0;
    while ((opt = getopt_long(argc, argv, opt_string, opts, NULL)) != -1) {
        switch (opt) {
            case 's':
                signer_file = optarg;
                break;
            case 'i':
                in_file = optarg;
                break;
            case 'o':
                out_file = optarg;
                break;
            case 't':
                ts_signer = optarg;
                break;
            case 'T':
                tsa_cnf = optarg;
                break;
            case 'p':
                password = optarg;
                break;
            case 'v':
                verbose = 1;
                break;
            default:
                verbose = 1;
                print_help(argv[0]);
                return -1;
        }
    }

    if (!password) {
        password = getenv("SIGN_PASSWORD");
    }
    if (password && strlen(password) != 32) {
        log_error("password length error, must 32 bytes, get %zu bytes", strlen(password));
        return -1;
    }
    if (verbose != 0) {
        log_debug("Parameters:");
        log_debug("  Signer certificate:             %s", signer_file);
        log_debug("  Input:                          %s", in_file);
        log_debug("  Output:                         %s", out_file);
    }
    return 0;
}

int main(int argc, char **argv)
{
    int ret = 0;
    ret     = arg_parse(argc, argv);
    if (ret != 0) {
        return ret;
    }
    ret = cms_sign();  // 正常签名后会生成cms_tmp_buf数据，该数据无需释放
    if (ret != 0) {
        return ret;
    }
    log_info("Parse cms data start");
    ret = asn_parse(0, (unsigned char *)cms_tmp_buf->data, cms_tmp_buf->length, &cms_ad);
    if (ret != 0) {
        return ret;
    }

    ret = timestamp_sign();
    if (ret != 0) {
        return ret;
    }
    log_info("Finished");
    return 0;
}
